<template>
  <nav
    ref="tocRef"
    class="toc-nav max-h-[500px] w-[inherit] overflow-y-auto rounded py-2 shadow-md dark:bg-neutral-800"
  >
    <!-- <div class="catalog-header">
      <svg-icon icon-class="category"></svg-icon>
      目录
    </div> -->
    <ul class="catalog-content px-4">
      <li
        v-for="(anchor, index) of titleList"
        :id="`toc-li-${anchor.lineIndex}`"
        :key="anchor.title"
        class="block overflow-hidden text-ellipsis whitespace-nowrap rounded p-2 hover:bg-gray-200 dark:hover:bg-indigo-500"
        :class="currentIndex === index ? 'active' : ''"
        :style="{ paddingLeft: `${anchor.indent * 10}px` }"
        @click="handleAnchorClick(anchor, index)"
      >
        {{ anchor.title }}
      </li>
    </ul>
  </nav>
</template>

<script setup lang="ts">
import { useScroll, watchThrottled } from '@vueuse/core'

const props = defineProps({
  domRef: {
    type: Object,
    default: null
  }
})

const titleList = ref<any>([])
const tocRef = ref<HTMLElement | null>(null)
const currentIndex = ref(0)
// 推荐文章dom高度
const recommendHeight = ref(0)
// 文章顶部固定图高度
const patternHeight = ref(0)

const getTitles = () => {
  const anchors = props.domRef.querySelectorAll('h1,h2,h3')
  const titles = Array.from(anchors).filter((t: any) => !!t.innerText.trim())
  if (!titles.length) titleList.value = []
  const hTags = Array.from(new Set(titles.map((t: any) => t.tagName))).sort()
  titleList.value = titles.map((el: any, index) => {
    el.dataset.id = index
    return {
      title: el.innerText,
      lineIndex: el.dataset.id,
      indent: hTags.indexOf(el.tagName)
    }
  })
}

// 点击锚点目录
function handleAnchorClick(anchor: any, index: number) {
  const heading = props.domRef.querySelector(`[data-id="${anchor.lineIndex}"]`)
  // const heading = preview.querySelector(`#${anchor.title}`)
  if (heading) {
    window.scrollTo({
      behavior: 'smooth',
      top: heading.offsetTop + patternHeight.value - 40
    })
    setTimeout(() => (currentIndex.value = index), 500)
  }
}

// * 实现目录高亮当前位置的标题
// 思路: 循环的方式将标题距离顶部距离与滚动条当前位置对比, 来确定高亮的标题
const { y } = useScroll(window)
watchThrottled(
  y,
  () => {
    titleList.value.forEach((titleItem: any, index: number) => {
      const heading = props.domRef.querySelector(`[data-id="${titleItem.lineIndex}"]`)
      // const tocNavDom = document.querySelector('.toc-nav') as HTMLElement
      if (y.value >= heading.offsetTop + patternHeight.value - 50) {
        // 比 40 稍微多一点
        currentIndex.value = index
        // 目录item滚动同步
        if (index > 1) {
          const tocLiId = document.getElementById(`toc-li-${titleItem.lineIndex}`)
          tocLiId &&
            tocLiId.scrollIntoView({
              block: 'center',
              inline: 'nearest',
              behavior: 'smooth'
            })
        }
      }
    })
  },
  { throttle: 200 }
)

// 固定目录
watchThrottled(y, () => {
  if (tocRef.value) {
    if (y.value > patternHeight.value + recommendHeight.value) {
      tocRef.value.style.position = 'fixed'
      tocRef.value.style.top = '80px'
    } else {
      tocRef.value.style.position = ''
      tocRef.value.style.top = ''
    }
  }
})

onMounted(() => {
  nextTick(() => {
    const recommendDom = document.querySelector('.recommend') as HTMLElement
    const patternDom = document.querySelector('.articlePattern') as HTMLElement
    if (recommendDom) recommendHeight.value = recommendDom?.offsetHeight ?? 0
    if (patternDom) patternHeight.value = patternDom?.offsetHeight ?? 0
    getTitles()
  })
})
</script>

<style>
.toc-nav ul li.active {
  @apply text-orange-500 dark:text-[#007fff];
}
</style>
